// https://threejsfundamentals.org/threejs/lessons/threejs-shadertoy.html
import { 
  BoxGeometry,
  Mesh,
  NearestFilter,
  PerspectiveCamera,
  RepeatWrapping,
  Scene,
  ShaderMaterial,
  TextureLoader,
  Vector3,
  WebGLRenderer 
} from "three"
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls'

const renderer = new WebGLRenderer({ antialias: true })
renderer.setClearColor(0xeeeeee)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)

const camera = new PerspectiveCamera(75, 2, 0.1, 5)
camera.position.z = 2

const secne = new Scene()

const geometry = new BoxGeometry(1, 1, 1)

// 顶点着色器
const vertexShader = `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
`

// 片段着色器
const fragmentShader = `
#include <common>

uniform vec3 iResolution;
uniform float iTime;
uniform sampler2D iChannel0;

#define TIMESCALE 0.25
// 定义每行几个
#define TILES 8
#define COLOR 0.7, 1.6, 2.8

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  vec2 uv = fragCoord.xy / iResolution.xy;
  uv.x *= iResolution.x / iResolution.y;

  vec4 noise = texture2D(iChannel0, floor(uv * float(TILES)) / float(TILES));
  float p = 1.0 - mod(noise.r + noise.g + noise.b + iTime * float(TIMESCALE), 1.0);
  p = min(max(p * 3.0 - 1.8, 0.1), 2.0);

  vec2 r = mod(uv * float(TILES), 1.0);
  r = vec2(pow(r.x - 0.5, 2.0), pow(r.y - 0.5, 2.0));
  p *= 1.0 - pow(min(1.0, 12.0 * dot(r, r)), 2.0);

  fragColor = vec4(COLOR, 1.0) * p;
}

varying vec2 vUv;

void main() {
  mainImage(gl_FragColor, vUv * iResolution.xy);
}
`

// 片段着色器
const fragmentShader2 = `
#include <common>

uniform vec2 iResolution;
uniform float iTime;

float box(in vec2 _st, in vec2 _size){
    _size = vec2(0.5) - _size*0.5;
    vec2 uv = smoothstep(_size,
                        _size+vec2(0.001),
                        _st);
    uv *= smoothstep(_size,
                    _size+vec2(0.001),
                    vec2(1.0)-_st);
    return uv.x*uv.y;
}

float cross(in vec2 _st, float _size){
    return  box(_st, vec2(_size,_size/4.)) +
            box(_st, vec2(_size/4.,_size));
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  vec2 st = fragCoord.xy / iResolution.xy;
  st.x *= iResolution.x / iResolution.y;

  vec3 color = vec3(0.0);

  // To move the cross we move the space
  vec2 translate = vec2(cos(iTime),sin(iTime));
  st += translate*0.35;

  // Show the coordinates of the space on the background
  // color = vec3(st.x,st.y,0.0);

  // Add the shape on the foreground
  color += vec3(cross(st,0.25));

  fragColor = vec4(color,1.0);
}

varying vec2 vUv;

void main(){
  mainImage(gl_FragColor, vUv * iResolution.xy);
}
`

const loader = new TextureLoader()
const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/bayer.png');
texture.minFilter = NearestFilter
texture.magFilter = NearestFilter
texture.wrapS = RepeatWrapping
texture.wrapT = RepeatWrapping

const uniforms = {
  iTime: { value: 0 },
  iResolution: { value: new Vector3(1, 1, 1)},
  iChannel0: { value: texture },
}
const material = new ShaderMaterial({
  vertexShader,
  fragmentShader,
  uniforms,
})

function makeInstance(geometry: any, x: number): Mesh {
  const cube = new Mesh(geometry, material)
  secne.add(cube)

  cube.position.x = x

  return cube
}

const cubes = [
  makeInstance(geometry, 0),
  makeInstance(geometry, -2),
  makeInstance(geometry, 2),
]

function resizeRendererToDisplaySize(renderer: any) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  const needResize = canvas.width !== width || canvas.height !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}

const trackballControls = new TrackballControls(camera, renderer.domElement)

function render(time: number) {
  trackballControls.update()

  time *= 0.001

  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }

  cubes.forEach((cube, ndx) => {
    const speed = 1 + ndx * .1
    const rot = time * speed

    cube.rotation.x = rot
    cube.rotation.y = rot
  })

  uniforms.iTime.value = time

  renderer.render(secne, camera)

  requestAnimationFrame(render)
}

requestAnimationFrame(render)